/*
Copyright 2011 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
*/

goog.provide('testing.KeyNav');

goog.require('goog.dom');
goog.require('goog.events.KeyHandler');

/**
 * Enable arrow based navigation through the provided collection of interactive
 * elements. This feature allows dpad devices that transmit arrow key events to
 * navigate a collection of interactive elements. It also transforms escape
 * key events into a history.go(-1) call, or "browser back button".
 *
 * NOTE 1: At least one dpad device (a Sony TV) dispatches arrow up/down events
 * AND moves focus within the page. Running this script in that environment
 * doubled the number of elements that would be traversed with an up/down arrow
 * click. This is why we only listen to left/right arrows. These also feel more
 * appropriate as being similar to tab/shift+tab.
 *
 * NOTE 2: This feature makes use of the document.body and history globals, so
 * it should not be used outside of a browser context.
 *
 * Example Usage:
 *
 *   // Loop through all anchor tags:
 *   testing.KeyNav('a');
 *
 *   // Loop through elements with class, 'foo'
 *   var elems = goog.dom.getElementsByClass('foo');
 *   testing.KeyNav(elems);
 *
 * @return {Array.<Element>} the collection of elements that were used.
 * @param {Array.<string>|string} interactiveElements can be an {Array} of dom
 * elements, or a single string node name. If a single string is provided, all
 * dom elements matching that node name will be included.
 */
testing.KeyNav = function(interactiveElements) {

  if (!interactiveElements) {
    return;
  }

  // If a node name was provided, find all matching elements:
  if (typeof(interactiveElements) == 'string') {
    interactiveElements = goog.dom.getElementsByTagNameAndClass('a');
  }

  // Constants
  var KeyCodes = {
    ESC: 27,
    LEFT: 37,
    RIGHT: 39
  }

  /**
   * Find the currently selected element and move focus to the next one.
   * @private
   */
  function selectNextElement() {

    var first = function(arr) {
      return arr[0];
    }

    var last = function(arr) {
      return arr[arr.length-1];
    }

    var nextIndex = function(index) {
      if (index === interactiveElements.length - 1) {
        return 0;
      }
      return index + 1;
    }

    var activeElement = document.activeElement;
    if (!activeElement) {
      first(interactiveElements).focus();
    } else {
      var len = interactiveElements.length;
      var anchor;
      for (var i = 0; i < len; i++) {
        anchor = interactiveElements[i];
        if (anchor === activeElement) {
          interactiveElements[nextIndex(i)].focus();
          return;
        }
      }
      first(interactiveElements).focus();
    }
  }

  /**
   * Find the currently selected element and move focus to the previous one.
   * @private
   */
  function selectPreviousElement() {

    var prevIndex = function(index) {
      if (index === 0) {
        return interactiveElements.length - 1;
      }
      return index - 1;
    }

    var activeElement = document.activeElement;
    if (!activeElement) {
      last(interactiveElements).focus();
    } else {
      var len = interactiveElements.length;
      var anchor;
      for (var i = 0; i < len; i++) {
        anchor = interactiveElements[i];
        if (anchor === activeElement) {
          interactiveElements[prevIndex(i)].focus();
          return;
        }
      }
      first(interactiveElements).focus();
    }
  }

  /**
   * Listen for key events on document.body
   */
  var documentKeyHandler = new goog.events.KeyHandler(document.body);
  goog.events.listen(documentKeyHandler, 'key', function(e) {
    //console.log('keydown with: ' + e.keyCode);
    switch(e.keyCode) {
      case KeyCodes.ESC:
        history.go(-1);
      case KeyCodes.RIGHT:
        selectNextElement();
        break;
      case KeyCodes.LEFT:
        selectPreviousElement();
        break;
    }
  });

  return interactiveElements;
}
